﻿// 
//    Copyright 2010 The JaernCloud Project
// 
//    Licensed under the Apache License, Version 2.0 (the "License");
//    you may not use this file except in compliance with the License.
//    You may obtain a copy of the License at
// 
//        http://www.apache.org/licenses/LICENSE-2.0
// 
//    Unless required by applicable law or agreed to in writing, software
//    distributed under the License is distributed on an "AS IS" BASIS,
//    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//    See the License for the specific language governing permissions and
//    limitations under the License.
// 
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using JaernCloud.Language.Compiler.Ast;
using System.IO;

namespace JaernCloud.Language.Compiler.Types {

    #region Nodes and Node Creation

    public interface INodeType : IEquatable<INodeType> {
        
        // TODO Use ninject mvc and ninject moq?
        // TODO Check http://github.com/schambers/fluentmigrator/wiki/migration for migration framework

        String Name { get; }
        List<String> Names { get; }
        List<INodeType> Children { get; }
        List<INodeType> InheritsFrom { get; }
        INodeType Parent { get; set; }

        AstNode SourceNode { get; }

        INodeType GetNodeTypeInstanceFor(AstNode node);
    }

    public interface INodeTypeCreator<N> where N : INodeType {
        N CreateNode();
    }

    public interface INodeTypeCreationContext<P> where P : INodeType {
        P Parent { get; }
    }

    #endregion

    #region Scopes, Persistence and Models

    /// <summary>
    /// Scope describes the lifecycle of a node instance created using a node type.
    /// </summary>
    /// <remarks>In order to use anything other than Prototype, the identity of the node must be unique. This is needed in order to save and restore the node.</remarks>
    public enum NodeTypeScope {
        
        /// <summary>
        /// New instance each time.
        /// </summary>
        Prototype,

        /// <summary>
        /// New instance per request
        /// </summary>
        Request,

        /// <summary>
        /// Same instance within an single request and all ajax requests originating from that request.
        /// New instance for each following non-ajax request.
        /// </summary>
        Conversation,

        /// <summary>
        /// Same instance with the current http session
        /// </summary>
        Session,

        /// <summary>
        /// Singleton within the current application. Primarily used for services.
        /// </summary>
        Application,

        /// <summary>
        /// Instance persisted in a repository
        /// </summary>
        Persisted,

        /// <summary>
        /// Default scope for nodes that don't provide a scope
        /// </summary>
        Default = Prototype
    }

    public interface IScopedNodeType : INodeType {
        
        NodeTypeScope Scope { get; }        
        
        /// <summary>
        /// Unique id used to save and load the node from the specified scope store.
        /// </summary>
        String ScopeNodeId { get; }

    }

    /// <summary>
    /// Provides an identifier scope used to perform node type lookups using node identifier tag selectors.
    /// Matches will be returned from the closest surrounding scope, starting with siblings.
    /// </summary>
    public interface INodeIdentifierScope {
        
        List<INodeType> Lookup(AstTagSelector selector);

        INodeType Lookup(AstExpression expression);

        INodeType Lookup(AstParameter parameter);

    }

    /// <summary>
    /// Selector which returns matching node types by traversing down the node type tree.
    /// </summary>
    public interface INodeSelector {

        IEnumerable<INodeType> Select(INodeType context, string selector);
        TNodeType Single<TNodeType>(INodeType context, string selector) where TNodeType : INodeType;

    }


    public interface IPersistedNodeType<N> : IScopedNodeType where N : INodeType {
        /// <summary>
        /// TODO: Check the new POCO capabilities in the new Entity Framework
        /// </summary>
        /// <returns></returns>
        N Save();
        void Delete();
    }

    public interface IModelNodeType<N> : INodeType, IScopedNodeType where N : INodeType {

    }

    #endregion

    #region Queries

    public interface IQueryNodeType : INodeType, IQueryProvider {
        
        /// <summary>
        /// The node type which owns the query and for which an instance is created for each query result
        /// </summary>
        INodeType Owner { get; set; }        
        
        /// <summary>
        /// Provides access to the query operator from which this node type was created
        /// </summary>
        AstQueryOperator QueryOperator { get; }
        
        /// <summary>
        /// A node type representing a query expression type or query by example type
        /// </summary>
        INodeType QueryOperatorType { get; }

        /// <summary>
        /// The expression used to resolve query results or null if the QueryKind is not Expression
        /// </summary>
        AstExpression QueryExpression { get; }
        
        /// <summary>
        /// The tag of a persistent type which is used to retrieve query results (model#id for example)
        /// </summary>
        AstTag QueryTag { get; }
        
        /// <summary>
        /// The kind of query this type represents
        /// </summary>
        QueryKind QueryKind { get; }

    }

    public enum QueryKind {
        Expression,
        TagWithId,
        ByExample
    }

    #endregion

    #region Pages

    public interface IPageNodeType : INodeType, IActionNodeTypeOwner, IScopedNodeType {
        // Request scope node? Should be yes, since it needs to be re-evaluated for each request??
        // Can handle incoming actions from the request
    }

    #endregion

    #region Views

    public interface IRenderNodeType : INodeType {
        void Render(INodeType root, TextWriter writer);
    }

    public interface IViewNodeType : IRenderNodeType, INodeType, IScopedNodeType {
    
    }

    #endregion

    #region Actions

    /// <summary>
    /// Used for page actions (incoming request), and for client action (clicks)
    /// </summary>
    public interface IActionNodeType : INodeType {
        String ActionName { get; }
        IActionNodeType Target { get; }
        bool IsAsync { get; }
        bool IsAjax { get; }
        bool HasAction { get; }
        void ExecuteAction(IActionNodeTypeOwner owner, IList<INodeType> arguments = null);
    }

    public interface IActionNodeTypeOwner : INodeType {
        IList<IActionNodeType> Actions { get; }
        void ExecuteAction(IActionNodeType action, IList<INodeType> arguments = null);
        void ExecuteAction(String actionName, IList<INodeType> arguments = null);
    }

    #endregion

    #region Events

    /// <summary>
    /// Annotates a node as a listener to a certain event
    /// </summary>
    public interface IEventNodeType : INodeType {
        String EventName { get; }
        void ExecuteEvent(IEventNodeTypeOwner owner, IList<INodeType> arguments = null);
    }

    public interface IEventNodeTypeOwner : INodeType {
        IList<IEventNodeType> Events { get; }
        void ExecuteEvent(IEventNodeType @event, IList<INodeType> arguments = null);
        void ExecuteEvent(String eventName, IList<INodeType> arguments = null);
    }

    #endregion

    #region Identifiers, Parameters, Constants and Expressions

    public interface IConstantNodeType : INodeType {
        
        // represents a constant declaration for value types
        
        bool IsString { get; }
        bool IsFloat { get; }
        bool IsDouble { get; }
        bool IsBool { get; }
        bool IsInt { get; }
        bool IsLong { get; }
        bool IsNull { get; }

        Type ValueType { get; }
        object Value { get; }
        T ValueAs<T> ();
    }

    /// <summary>
    /// Represents an identifier declaration for non-built in node types
    /// </summary>
    public interface IIdentifierNodeType : INodeType {

    }

    /// <summary>
    /// Represents a parameter declaration or reference
    /// </summary>
    public interface IParameterNodeType : INodeType {
    }

    /// <summary>
    /// Represents and expression  declaration
    /// </summary>
    public interface IExpressionNodeType : INodeType {
        AstExpression Expression { get; }
    }

    #endregion

}
